<?php

abstract class AModel extends CModel
{
	// ------------------------------------------------------------------------------------

	protected $_tableName = NULL;
	protected $_tableColumnPrefix = NULL;

	protected $_attributes = array();
	protected $_attributes_to_save = array();
	
	protected $_attributeTypes = NULL;

	// ------------------------------------------------------------------------------------
	/**
	 * @static
	 * @return CDbConnection
	 */
	protected static $_db = NULL;
	public static function db()
	{
		return NULL === self::$_db ? self::$_db = Yii::app()->getDb() : self::$_db;
	}
	// ------------------------------------------------------------------------------------
	/**
	 * @throws Exception
	 * @param array $attributes
	 * @return void
	 */
	public function __construct($attributes = array())
	{
		if(empty($this->_tableName))
		{
			throw new Exception('No tableName for class ' . __CLASS__);
		}
		$this->_attributes = $attributes;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return array
	 */
	public function attributeNames()
	{
		return array();
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return array
	 */
	public function attributeTypes()
	{
		if(NULL === $this->_attributeTypes)
		{
			$this->_attributeTypes = self::_getAttributeTypes($this->_tableName);
		}
		return $this->_attributeTypes;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return array
	 */
	public function toArray()
	{
		return $this->_attributes;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @static
	 * @param  $table
	 * @return array
	 */
	protected static function _getAttributeTypes($table)
	{
		$result = array();
		$query = self::db()->createCommand('DESCRIBE ' . $table)->query();
		foreach($query as $row)
		{
			$data = array(
				'key' => $row['Key'],
				'type' => $row['Type']
			);

			if(FALSE !== ($pos = strpos($data['type'], '(')))
			{
				$data['type'] = substr($data['type'], 0, $pos);
			}

			switch($data['type'])
			{
				case 'tinyint':
				case 'int':
					$data['type'] = 'integer';
				break;

				case 'char':
				case 'varchar':
				case 'text':
					$data['type'] = 'string';
				break;
			}

			$result[$row['Field']] = $data;
		}

		return $result;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @param  $name
	 * @return array
	 */
	public function __get($name)
	{
		$method = 'get' . str_replace('_', '', $name);
		if(method_exists($this, $method))
		{
			return $this->$method();
		}

		if(isset($this->_attributes[$name]))
		{
			return $this->_attributes[$name];
		}

		$name = $this->_tableColumnPrefix . $name;

		if(isset($this->_attributes[$name]))
		{
			return $this->_attributes[$name];
		}

		return NULL;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @param  $name
	 * @param  $value
	 * @return
	 */
	public function __set($name, $value)
	{
		$method = 'set' . $name;
		if(method_exists($this, $method))
		{
			return $this->$method($value);
		}
		elseif(!isset($this->_attributes[$name]) || $this->_attributes[$name] !== $value)
		{
			$this->_attributes[$name] = $value;
			$this->_attributes_to_save[] = $name;
		}
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @param  $name
	 * @return
	 */
	public function __isset($name)
	{
		$method = 'get' . str_replace('_', '', $name);
		return isset($this->_attributes[$name]) || method_exists($this, $method);
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return 
	 */
	public function refresh()
	{
		$sql = '
SELECT * FROM ' . self::db()->quoteTableName($this->_tableName) . '
WHERE `' . $this->_tableColumnPrefix . 'id` = :row_id
LIMIT 1';
		$data = self::db()->createCommand($sql)->queryRow(true, array('row_id' => $this->_attributes[$this->_tableColumnPrefix . 'id']));
		if(!empty($data))
		{
			$this->_attributes = $data;
		}
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return bool
	 */
	public function save($conditions = NULL, $params = NULL)
	{
		if(!$this->validate())
		{
			return false;
		}

		if(!$this->beforeSave())
		{
			return false;
		}

		if(!isset($this->_attributes[$this->_tableColumnPrefix . 'id']))
		{
			$result = $this->insert();
		}
		else
		{
			$result = $this->update($conditions, $params);
		}

		if($result)
		{
			$this->afterSave();
			return $result;
		}

		return false;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return bool
	 */
	public function insert()
	{
		if(empty($this->_attributes))
		{
			return true;
		}

		if(self::db()->createCommand()->insert($this->_tableName, $this->_attributes))
		{
			$this->_attributes_to_save = array();

			if(!isset($this->_attributes[$this->_tableColumnPrefix . 'id']))
			{
				$this->_attributes[$this->_tableColumnPrefix . 'id'] = self::db()->getLastInsertID();
			}
			return true;
		}

		return false;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return bool
	 */
	public function replace()
	{
		if(empty($this->_attributes))
		{
			return true;
		}

		$params = array();
		$names = array();
		foreach($this->_attributes as $name => $value)
		{
			$names[] = self::db()->quoteColumnName($name);
			$params[':' . $name] = $value;
		}

		$sql = 'REPLACE ' . self::db()->quoteTableName($this->_tableName)
		. ' (' . implode(', ', $names) . ') VALUES ('
		. implode(', ', array_keys($params)) . ')';

		if($this->db()->createCommand($sql)->execute($params))
		{
			$this->_attributes_to_save = array();
			return true;
		}

		return false;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @param  $conditions
	 * @param  $params
	 * @return bool
	 */
	public function update($conditions = NULL, $params = NULL)
	{
		if(empty($this->_attributes_to_save))
		{
			return true;
		}

		$set = array();
		$this->_attributes_to_save = array_unique($this->_attributes_to_save);
		foreach($this->_attributes_to_save as $key)
		{
			$set[$key] = $this->_attributes[$key];
		}

		if(NULL === $conditions)
		{
			$conditions = '`' . $this->_tableColumnPrefix . 'id` = :' . $this->_tableColumnPrefix . 'id';
			$params[':' . $this->_tableColumnPrefix . 'id'] = $this->_attributes[$this->_tableColumnPrefix . 'id'];
		}

		if(self::db()->createCommand()->update($this->_tableName, $set, $conditions, $params))
		{
			$this->_attributes_to_save = array();
			return true;
		}

		return false;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return bool
	 */
	public function delete()
	{
		$key = $this->_tableColumnPrefix . 'id';
		if(!empty($this->_attributes[$key]))
		{
			return !!self::db()->createCommand('DELETE FROM ' . self::db()->quoteTableName($this->_tableName) . ' WHERE `' . $key . '` = :row_id LIMIT 1' )->execute(array('row_id' => $this->_attributes[$key]));
		}
		return false;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return bool
	 */
	protected function beforeSave()
	{
		return true;
	}

	// ------------------------------------------------------------------------------------
	/**
	 * @return void
	 */
	protected function afterSave()
	{
		return true;
	}

	// ------------------------------------------------------------------------------------
}